import javax.swing.*;
import javax.swing.plaf.FontUIResource;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.Random;

/**
 * @author cocoa
 */
public class GamePanel extends JPanel implements ActionListener {

    private static final int ROWS = 4;
    private static final int COLS = 4;
    private JFrame frame = null;
    private GamePanel panel = null;
    private final Card[][] cards = new Card[GamePanel.ROWS][GamePanel.COLS];


    public GamePanel(JFrame frame) {
        this.setLayout(null);
        this.setOpaque(false);

        this.frame = frame;
        this.panel = this;

        // 创建菜单
        this.createMenu();

        // 创建卡片
        this.initCard();

        // 随机创建一个卡片
        this.createRandomNum();

        //创建键盘监听
        this.createKeyListener();

    }

    private void createKeyListener() {
        KeyAdapter keyAdapter = new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) {
                super.keyPressed(e);
                int key = e.getKeyCode();
                switch (key) {
                    case KeyEvent.VK_UP:
                    case KeyEvent.VK_W:
                        GamePanel.this.moveCard("up");
                        break;
                    case KeyEvent.VK_DOWN:
                    case KeyEvent.VK_S:
                        GamePanel.this.moveCard("down");
                        break;
                    case KeyEvent.VK_LEFT:
                    case KeyEvent.VK_A:
                        GamePanel.this.moveCard("left");
                        break;
                    case KeyEvent.VK_RIGHT:
                    case KeyEvent.VK_D:
                        GamePanel.this.moveCard("right");
                        break;
                    default:
                        break;
                }
            }
        };

        this.frame.addKeyListener(keyAdapter);
    }

    /**
     * 移动卡片
     * @param dir
     */
    private void moveCard(String dir) {
        // 清理所有卡片的标记改为false
        this.clearCard();

        if ("up".equals(dir)) {
            System.out.println("up");
            this.moveCardTop();
        } else if ("down".equals(dir)) {
            System.out.println("down");
            this.moveCardDown();
        } else if ("left".equals(dir)) {
            System.out.println("left");
            this.moveCardLeft();
        } else {
            System.out.println("right");
            this.moveCardRight();
        }
        // 重新创建新的卡片
        this.createRandomNum();


        // 调用JPanel，重新绘制画布
        this.repaint();

        // 判断游戏是否结束的
        this.gameOverOrNot();
    }

    private void gameOverOrNot() {
        /**
         * 结束条件
         * 1. 位置满了
         * 2. 4个方向都没有可以合并的卡片
         */
        if (this.isWin()) {
            this.gameWin();

            // 如果位置满了的话
        } else if (this.cardIsFull()) {
            // TODO: 判断是否还能移动的逻辑
            this.gameOver();
        }
    }

    // 游戏结束
    private void gameOver() {
        UIManager.put("OptionPane.buttonFont", new FontUIResource(new Font("思源宋体", Font.ITALIC, 18)));
        UIManager.put("OptionPane.messageFont", new FontUIResource(new Font("思源宋体", Font.ITALIC, 18)));
        JOptionPane.showMessageDialog(this.frame, "你失败了，请再接再厉了！");
    }

    // 游戏胜利
    private void gameWin() {
        UIManager.put("OptionPane.buttonFont", new FontUIResource(new Font("思源宋体", Font.ITALIC, 18)));
        UIManager.put("OptionPane.messageFont", new FontUIResource(new Font("思源宋体", Font.ITALIC, 18)));
        JOptionPane.showMessageDialog(this.frame, "你胜利了，太棒了！");
    }

    /**
     *  判断是否胜利
     *
     * @return boolean
     */
    private boolean isWin() {
        Card card;
        for (int i = 0; i < GamePanel.ROWS; i++) {
            for (int j = 0; j < GamePanel.COLS; j++) {
                card = this.cards[i][j];

                if (card.getNum() == 2048) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 清理所有卡片的标记改为false
     */
    private void clearCard() {
        Card card;
        for (int i = 0; i < GamePanel.ROWS; i++) {
            for (int j = 0; j < GamePanel.COLS; j++) {
                card = this.cards[i][j];
                card.setMerge(false);
            }
        }
    }

    private void moveCardRight() {
        Card card;
        for (int i = GamePanel.ROWS - 2; i >= 0; i--) {
            for (int j = GamePanel.COLS - 2; j >= 0; j--) {
                card = this.cards[i][j];

                // 只要卡片不是空白的就要移动
                if (card.getNum() != 0) {
                    card.moveRight(this.cards);
                }
            }
        }
    }

    private void moveCardLeft() {
        Card card;
        for (int i = 0; i < GamePanel.ROWS; i++) {
            for (int j = GamePanel.COLS - 3; j < GamePanel.COLS; j++) {
                card = this.cards[i][j];

                // 只要卡片不是空白的就要移动
                if (card.getNum() != 0) {
                    card.moveLeft(this.cards);
                }
            }
        }
    }

    private void moveCardDown() {
        Card card;
        for (int i = 0; i < GamePanel.ROWS; i++) {
            for (int j = 0; j < GamePanel.COLS; j++) {
                card = this.cards[i][j];

                // 只要卡片不是空白的就要移动
                if (card.getNum() != 0) {
                    card.moveDown(this.cards);
                }
            }
        }
    }

    private void moveCardTop() {
        Card card;
        for (int i = 1; i < GamePanel.ROWS; i++) {
            for (int j = 0; j < GamePanel.COLS; j++) {
                card = this.cards[i][j];

                // 只要卡片不是空白的就要移动
                if (card.getNum() != 0) {
                    card.moveTop(this.cards);
                }
            }
        }
    }

    private void createRandomNum() {
        // 随机显示数字2或4
        int num = 0;
        Random random = new Random();
        // 随机数是0-4，+1是为了随机到1-5
        int n = random.nextInt(5) + 1;
        if (n == 1) {
            num = 4;
        } else {
            num = 2;
        }

        // 如果格子满了，则不需要再获取了
        if (this.cardIsFull()) {
            return;
        }
        // 取到卡片
        Card card = this.getRandomCard(random);
        if (card != null) {
            card.setNum(num);
        }
    }

    private boolean cardIsFull() {
        Card card;
        for (int i = 0; i < GamePanel.ROWS; i++) {
            for (int j = 0; j < GamePanel.COLS; j++) {
                card = this.cards[i][j];
                if (card.getNum() == 0) {
                    return false;
                }
            }
        }

        return true;
    }

    private Card getRandomCard(Random random) {
        int i = random.nextInt(GamePanel.ROWS);
        int j = random.nextInt(GamePanel.COLS);
        Card card = this.cards[i][j];

        // 如果是空白卡片，则直接返回
        if (card.getNum() == 0) {
            return card;
        }

        // 没有找到，则递归继续寻找
        return this.getRandomCard(random);
    }

    // 初始化卡片
    private void initCard() {
        Card card;
        for (int i = 0; i < GamePanel.ROWS; i++) {
            for (int j = 0; j < GamePanel.COLS; j++) {
                card = new Card(i, j);
                this.cards[i][j] = card;
            }
        }
    }

    @Override
    public void paint(Graphics g) {
        super.paint(g);
        // 绘制卡片
        this.drawCard(g);

    }

    // 绘制卡片
    private void drawCard(Graphics g) {
        Card card;
        for (int i = 0; i < GamePanel.ROWS; i++) {
            for (int j = 0; j < GamePanel.COLS; j++) {
                card = this.cards[i][j];
                // 开始画图
                card.draw(g);
            }
        }
    }

    // 创建菜单
    private void createMenu() {
        // 创建字体
        Font font = createFont();
        // 创建菜单栏
        JMenuBar jmb = new JMenuBar();
        JMenu game = new JMenu("游戏");
        game.setFont(font);
        // 创建游戏菜单的子项
        JMenuItem newGame = new JMenuItem("新游戏");
        newGame.setFont(font);
        JMenuItem quit = new JMenuItem("退出");
        quit.setFont(font);

        // 把元素添加菜单
        game.add(newGame);
        game.add(quit);

        JMenu help = new JMenu("帮助");
        help.setFont(font);
        // 创建帮助菜单的子项
        JMenuItem operationHelp = new JMenuItem("操作帮助");
        operationHelp.setFont(font);
        JMenuItem winCondition = new JMenuItem("胜利条件");
        winCondition.setFont(font);

        help.add(operationHelp);
        help.add(winCondition);


        jmb.add(game);
        jmb.add(help);

        // 把菜单栏加入窗体
        this.frame.setJMenuBar(jmb);


        // 编写逻辑，事件监听
        newGame.addActionListener(this);
        quit.addActionListener(this);
        operationHelp.addActionListener(this);
        winCondition.addActionListener(this);

        newGame.setActionCommand("newGame");
        quit.setActionCommand("quit");
        operationHelp.setActionCommand("operationHelp");
        winCondition.setActionCommand("winCondition");
    }

    private static Font createFont() {
        // Font 类是用于设置图形用户界面上的字体样式的，包括字体类型(例如宋体、仿宋、Times New Roman 等)、字体风格(例如斜体字、加粗等)、以及字号大小。
        // Font.PLAIN(普通) Font.BOLD(加粗) Font.ITALIC(斜体)
        return new Font("思源宋体", Font.BOLD, 18);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        String command = e.getActionCommand();
        if ("newGame".equals(command)) {
            System.out.println("新游戏");
            this.newGame();
        } else if ("quit".equals(command)) {
            System.out.println("退出");
            Object[] options = {"确定", "取消"};
            int res = JOptionPane.showOptionDialog(
                    null,
                    "你确定要退出吗？",
                    "提示",
                    JOptionPane.YES_OPTION,
                    JOptionPane.QUESTION_MESSAGE,
                    null,
                    options,
                    options[0]
            );
            if (res == 0) {
                System.exit(0);
            }
        } else if ("operationHelp".equals(command)) {
            System.out.println("操作帮助");
            JOptionPane.showMessageDialog(
                    null,
                    "通过键盘上下左右来移动，相同的数字会合并！",
                    "提示",
                    JOptionPane.INFORMATION_MESSAGE
            );
        } else {
            System.out.println("胜利条件");
            JOptionPane.showMessageDialog(
                    null,
                    "得到数字2048获得胜利，当没有空卡片则失败！",
                    "提示",
                    JOptionPane.INFORMATION_MESSAGE
            );
        }

    }

    // 新游戏
    private void newGame() {
    }
}
